Upsert Content database rows by stable key - #2582
Conversation
|
Here's a visual recap of what changed: Open the full interactive recap |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Addressed the Builder review against
Verification on the repaired head: 88 targeted tests across 15 files, Content typecheck, additive-migration guard, unscoped-query guard, formatting, Hosted separate-worker/PostgreSQL contention remains an explicit deployment acceptance gate.
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Addressed the incremental Builder review on
Verification on the repaired head: 133 targeted tests across five persistence/editor files, Content typecheck, additive-migration guard, unscoped-query guard, formatting, and Hosted authenticated invocation, migration application, exact private-row readback, and separate-process/PostgreSQL contention remain explicit acceptance gates before unattended use.
|
|
Addressed the latest Builder finding on
Verification on the repaired head: 135 targeted tests across five persistence/editor files, Content typecheck, additive-migration guard, unscoped-query guard, formatting, and Hosted authenticated invocation, migration application, exact private-row readback, and separate-process/PostgreSQL contention remain explicit acceptance gates before unattended use.
|
|
Addressed the latest Builder findings on
Verification on the repaired head: 137 targeted tests across five persistence/editor files, Content typecheck, additive-migration guard, unscoped-query guard, formatting, and Hosted authenticated invocation, migration application, exact private-row readback, and separate-process/PostgreSQL contention remain explicit acceptance gates before unattended use.
|
…key-3192 # Conflicts: # templates/content/actions/set-document-property.ts # templates/content/parity/matrix.md # templates/content/parity/matrix.ts
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Builder reviewed your changes and found 3 potential issues 🔴
Review Details
Incremental Code Review Summary
The latest head fixes both previously reported source lifecycle issues: binding now verifies that the field/source updates affected rows, and source-role conversion deletes claims together with removed memberships. I resolved both stale threads before this pass. The exact readback-body comment remains open and was not reposted.
The remaining implementation has solid transaction-scoped cleanup for owned database deletion and comprehensive claim validation, but three new concurrency/lifecycle issues remain. Stable-key upsert currently rejects source ownership only for the key property, allowing caller writes to overwrite a different source-bound property. The unbind path still runs outside the binding transaction and can race a bind backfill. Finally, permanent deletion locks databases owned by documents in the subtree but not parent databases that own ordinary row documents, leaving a claim-orphan race for row deletion.
Risk classification: standard (shared business logic and persistent data mutation).
Key findings:
- 🔴 HIGH — Permanent deletion does not lock parent databases for ordinary row documents.
- 🟡 MEDIUM — Upsert can overwrite a non-key source-bound property.
- 🟡 MEDIUM — Unbind is not serialized with bind/backfill.
🧪 Browser testing: Skipped — PR changes persistence/actions and tests, with no user-facing UI implementation changes.

Problem
Content can create database rows, but it cannot safely project an external record into the same row across retries.
add-database-itemalways creates a fresh identity, so unattended imports and projections can duplicate rows or lose the connection between a source key and the Content item it created.Concurrent callers also need an unambiguous receipt. Without serialization through the post-commit readback, a later writer can overtake the first and make a successfully committed mutation return an internal error.
Approach
Add one generic, authenticated Content action that upserts a row by a caller-supplied stable string key. The database owns the identity fence; the action owns access checks, transactional row mutation, explicit
created,updated, orunchangedreceipts, and bounded readback.The action is source-agnostic and does not invoke Content's built-in AI for deterministic writes.
What changed
upsert-database-item-by-key, returning a stable item/document identity and exact bounded readback.(database, key property, key value)and one active value per(database, key property, row).Safety and operations
add-database-itembehavior is unchanged.Product roadmap impact
This fulfills part of the roadmap's shared-Action contract. It does not implement Content's Event or Rule model, public client packaging, scheduled automation, or a Source synchronization policy, so no roadmap record changes state in this PR.
Verification
git diff --checkare clean.401without authentication and403for an authenticated non-owner, with no denied stable-key claims written.200, all returned one stable item/document identity, and every receipt's readback matched that caller's requested payload.get-content-database(five rows per page) andget-documentreturned the exact accepted row and body. Direct PostgreSQL verification found exactly one claim, item, document, and key value for each exercised identity.Review focus
Follow-ups